﻿/*
Copyright (c) 2012 Maksim Gordeev

Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

 */

/*
 * Сделано в SharpDevelop.
 * Пользователь: maxxadm
 * Дата: 24.07.2012
 * Время: 17:30
 * 
 * Для изменения этого шаблона используйте Сервис | Настройка | Кодирование | Правка стандартных заголовков.
 * 
 * DEPRECATED
 * 
 */
using System;
using System.Net;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;
using System.IO;
using Hammock;

namespace jellybird.core
{
	/// <summary>
	/// Description of TwImage.
	/// </summary>
	public static class TwImage_deprecated
	{
		static TwImage_deprecated()
		{
			Timeout=10000;
			BufferLen=512;
			
			image_lists_intern.Add(new ImageList());
			image_lists_intern.Add(new ImageList());
			image_lists_intern.Add(new ImageList());
			image_lists_intern[(int)TwImageSize.mini].ImageSize=Helper.TwSize2Size(TwImageSize.mini);
			image_lists_intern[(int)TwImageSize.mini].ColorDepth=ColorDepth.Depth24Bit;
			image_lists_intern[(int)TwImageSize.normal].ImageSize=Helper.TwSize2Size(TwImageSize.normal);
			image_lists_intern[(int)TwImageSize.normal].ColorDepth=ColorDepth.Depth24Bit;
			image_lists_intern[(int)TwImageSize.bigger].ImageSize=Helper.TwSize2Size(TwImageSize.bigger);
			image_lists_intern[(int)TwImageSize.bigger].ColorDepth=ColorDepth.Depth24Bit;
		}
		
		public static void Dispose()
		{
			foreach(ImageList list in image_lists_intern)
			{
				if(list!=null)
					list.Dispose();
			}
		}
		
		public static event EventHandler<TwImageReadyEventArgs> ImageReady;
		
		private static List<ImageList> image_lists_intern=new List<ImageList>();
		private static object image_lists_lock=new object();
		
		public static int Timeout { get; set; }
		public static int BufferLen { get; set; }
		
		private static void on_image_ready(TwImageReadyEventArgs e, EventHandler<TwImageReadyEventArgs> user_callback)
		{
			if(ImageReady!=null)
				ImageReady.Invoke(e.State,e);
			
			if(user_callback!=null)
				user_callback.Invoke(e.State,e);
		}
		
		private static int try_find_image(string key,out ImageList image_list)
		{
			int ret=-1;
			image_list=null;
			
			lock(image_lists_lock)
			{
				foreach(ImageList list in image_lists_intern)
				{
					ret=list.Images.IndexOfKey(key);
					if(ret!=-1)
					{
						image_list=list;
						break;
					}
				}
			}
			
			return ret;
		}
		
		private static int try_add_image(string key,Image image,out ImageList image_list)
		{
			TwImageSize tw_size=Helper.Size2TwSize(image.Size);
			int ret=-1;
			if(tw_size!=TwImageSize.original)
			{
				//потому что вызывается из коллбэка в неосновном потоке
				lock(image_lists_lock)
				{
					image_lists_intern[(int)tw_size].Images.Add(key,image);
					image_list=image_lists_intern[(int)tw_size];
					ret=image_list.Images.IndexOfKey(key);
				}
			}
			else
			{
				image_list=null;
			}
			return ret;
		}
		
		private static void callback_read_proc(IAsyncResult ar)
		{
			//пртняли в буфер порцию данных
			State_internal state_intern=ar.AsyncState as State_internal;
			if(state_intern==null)
				throw new ArgumentException("Unexcepted type of async result.");
			
			try
			{
				int bytes_readed=state_intern.ResponseStream.EndRead(ar);
				if(bytes_readed>0)
				{
					//что то записалось в буфер
					state_intern.ResponseData.Write(state_intern.Buffer,0,bytes_readed);
					//и продолжаеи чтение
					IAsyncResult async_res=state_intern.ResponseStream.BeginRead
						(state_intern.Buffer,
						 0,
						 BufferLen,
						 new AsyncCallback(callback_read_proc),
						 state_intern);
				}
				else
				{
					try
					{
						//чтение завершилось
						state_intern.Response.Close();
						Image image=Image.FromStream(state_intern.ResponseData);
						ImageList image_list=null;
						int image_ind=try_add_image(state_intern.UrlOrKey,image,out image_list);
						if((image_ind!=-1)&&(image_list!=null))
						{
							on_image_ready
								(new TwImageReadyEventArgs(image_list,image_ind,state_intern.UserState),
								 state_intern.UserCallback);
						}
						else
						{
							on_image_ready
								(new TwImageReadyEventArgs(image,state_intern.UserState),
								 state_intern.UserCallback);
						}
					}
					catch(Exception ex_intern)
					{
						on_image_ready
							(new TwImageReadyEventArgs(ex_intern,state_intern.UserState),
							 state_intern.UserCallback);
					}
					finally
					{
						state_intern.Response.Close();
					}
				}
			}
			catch (Exception ex)
			{
				state_intern.Response.Close();
				on_image_ready
					(new TwImageReadyEventArgs(ex,state_intern.UserState),
					 state_intern.UserCallback);
			}
		}
		
		private static void callback_response_image_proc(IAsyncResult ar)
		{
			//получили ответ от сервера
			State_internal state_intern=ar.AsyncState as State_internal;
			if(state_intern==null)
				throw new ArgumentException("Unexcepted type of async result.");
			
			WebResponse response=null;
			try
			{
				response=state_intern.Request.EndGetResponse(ar);
				Stream resp_stream=response.GetResponseStream();
				if(resp_stream!=null)
				{
					state_intern.Response=response;
					state_intern.ResponseStream=resp_stream;
					//и начинаем читать из потока ответа
					IAsyncResult read_res=resp_stream.BeginRead
						(state_intern.Buffer,
						 0,
						 BufferLen,
						 new AsyncCallback(callback_read_proc),
						 state_intern);
				}
				else
				{
					//нет ответного потока
					response.Close();
					on_image_ready
						(new TwImageReadyEventArgs(new WebException("No response stream"),state_intern.UserState),
						 state_intern.UserCallback);
				}
			}
			catch(Exception ex)
			{
				if(response!=null)
					response.Close();
				on_image_ready
					(new TwImageReadyEventArgs(ex,state_intern.UserState),
					 state_intern.UserCallback);
			}
		}
		
		private static void callback_url_query_response
			(RestRequest request,
			 RestResponse response,
			 object state)
		{
			#if DEBUG
			Helper.OnDebugMessage(string.Format("Receive response {0}",request.Info));
			#endif
			State_internal state_intern=state as State_internal;
			if(state_intern==null)
				throw new ArgumentException("Unexcepted type of async result.");
			
			try
			{
				//ожидаем ответ 302
				#if DEBUG
				Helper.OnDebugMessage(string.Format("Status code={0}",response.StatusCode));
				#endif
				if(response.StatusCode==HttpStatusCode.Found)
				{
					string image_url=response.Headers["Location"];
					if(!string.IsNullOrEmpty(image_url))
					{
						//Теперь кэшируем и отдаём картинку
						//по полученному url
						WebRequest web_req=WebRequest.Create(image_url);
						web_req.Timeout=Timeout;
						state_intern.Request=web_req;
						IAsyncResult async_res=web_req.BeginGetResponse
							(new AsyncCallback(callback_response_image_proc),state_intern);
						//и всё дальнейшее - в соотв. коллбэке
					}
					else
					{
						//нету хидера Location
						on_image_ready
							(new TwImageReadyEventArgs(new WebException("Unexpected http header."),state_intern.UserState),
							 state_intern.UserCallback);
					}
				}
				else
				{
					//ответ не 302
					on_image_ready
						(new TwImageReadyEventArgs(new WebException("Unexpected status code."),state_intern.UserState),
						 state_intern.UserCallback);
				}
			}
			catch(Exception ex)
			{
				on_image_ready
					(new TwImageReadyEventArgs(ex,state_intern.UserState),
					 state_intern.UserCallback);
			}
			finally
			{
				if (response!=null)
					response.Dispose();
			}
		}
		
		public static void GetProfileImageCachedAsync
			(string screen_name,
			 TwImageSize size,
			 object user_state,
			 RestClient client,
			 EventHandler<TwImageReadyEventArgs> user_callback)
		{
			//как url для кэша будем использовать строку типа @<screen_name>
			//чтобы не ходить каждый раз за url на твиттер
			
			string fake_url=string.Format("@{0}",screen_name);
			
			//смотрим в кэш, если изображение - не Original
			if(size!= TwImageSize.original)
			{
				ImageList cached_list=image_lists_intern[(int)size];
				int cached_ind=cached_list.Images.IndexOfKey(fake_url);
				if(cached_ind>=0)
				{
					#if DEBUG
					Helper.OnDebugMessage(string.Format("Return image of {0} from cache",screen_name));
					#endif
					//есть в кэше
					on_image_ready(new TwImageReadyEventArgs(cached_list,cached_ind,user_state),user_callback);
					return;
				}
			}
			
			//в кэше нет
			//нужно получить url
			//и из него - картинку
			RestRequest request=new RestRequest()
			{
				Method=Hammock.Web.WebMethod.Get,
				Path=string.Format(@"{0}/{1}",Constants.USERS,Constants.PROFILE_IMAGE)
			};
			request.AddParameter(Constants.SCREEN_NAME,screen_name);
			request.AddParameter(Constants.SIZE,size.ToString());
			
			State_internal state_intern=new TwImage_deprecated.State_internal()
			{
				Buffer=new byte[BufferLen],
				Request=null,
				ResponseData=new MemoryStream(),
				UrlOrKey=fake_url,
				UserCallback=user_callback,
				UserState=user_state
			};
			#if DEBUG
			Helper.OnDebugMessage
				(string.Format("Begin query for profile image url {0}",screen_name));
			#endif
			IAsyncResult ar=client.BeginRequest
				(request,
				 new RestCallback(callback_url_query_response),
				 state_intern);
			//остальное в коллбэках
		}
		
		public static void GetImageCachedAsync
			(string url, object user_state, EventHandler<TwImageReadyEventArgs> user_callback)
		{
			
			ImageList cache_list=null;
			int cach_ind=try_find_image(url, out cache_list);
			if((cach_ind!=-1)&&(cache_list!=null))
			{
				#if DEBUG
				Helper.OnDebugMessage(string.Format("Return image of {0} from cache",url));
				#endif
				//есть в кэше
				TwImageReadyEventArgs sync_res=new TwImageReadyEventArgs
					(cache_list,cach_ind,user_state);
				on_image_ready(sync_res,user_callback);
				//и всё
				return;
			}
			
			//в кэше нет:
			//получаем асинхронно ответ
			WebRequest web_req=WebRequest.Create(url);
			web_req.Timeout=Timeout;
			State_internal state_intern=new TwImage_deprecated.State_internal()
			{
				UserCallback=user_callback,
				UserState=user_state,
				Request=web_req,
				Buffer=new byte[BufferLen],
				ResponseData=new MemoryStream(),
				UrlOrKey=url
			};
			#if DEBUG
			Helper.OnDebugMessage("Begin image download...");
			#endif
			IAsyncResult async_res=web_req.BeginGetResponse(new AsyncCallback(callback_response_image_proc),state_intern);
		}
		
		private class State_internal
		{
			public object UserState { get; set; }
			public EventHandler<TwImageReadyEventArgs> UserCallback { get; set; }
			public WebRequest Request { get; set; }
			public byte[] Buffer { get; set; }
			public MemoryStream ResponseData { get; set; }
			public WebResponse Response { get; set; }
			public Stream ResponseStream { get; set; }
			public string UrlOrKey { get; set; }
		}
	}
	
	public sealed class TwImageReadyEventArgs:EventArgs
	{
		public TwImageReadyEventArgs(ImageList image_list,int image_index,object state)
		{
			ImageList=image_list;
			ImageIndex=image_index;
			Success=true;
			Exception=null;
			State=state;
			Image=ImageList.Images[ImageIndex];
		}
		
		public TwImageReadyEventArgs(Exception exception,object state)
		{
			Exception=exception;
			Success=false;
			State=state;
		}
		
		public TwImageReadyEventArgs(Image image,object state)
		{
			Image=image;
			State=state;
		}
		
		public System.Windows.Forms.ImageList ImageList { get; private set; }
		public int ImageIndex { get; private set; }
		public bool Success { get; private set; }
		public Exception Exception { get; private set; }
		public object State { get; private set; }
		public Image Image{get; private set;}
	}
}
